/*
Copyleft 2006 by T.S.Yo
*/

package exp.regression;
import exp.util.*;
import ec.app.regression.*;
import ec.*;
import ec.util.*;
import ec.coevolve.*;
import ec.vector.*;
import ec.simple.*;
import ec.gp.*;
import ec.gp.koza.*;
//import java.io.*;

/* 
 * MyRegression.java
 * 
 * Created: 2006.11.03
 * By: T.S.Yo
 */

/**
 * MyRegression extends Regression for Koza (quartic) Symbolic Regression problem.
 *
 * @author T.S.Yo
 * @version 1.0 
 */

public class MyRegression extends Regression implements GroupedProblemForm
{
//----------------------------------------------------------------------
/**
 * MyRegression implements a Symbolic Regression problem.
 *
 * <p>The equation to be regressed is y = x^6 - 2x^4 + x^2, {x in [-1,1]}
 * <p>This equation was introduced in J. R. Koza, GP II, 1994.
*/
    public float func(float x)
        { return x*x*x*x*x*x - 2.0F*x*x*x*x + x*x; }
//----------------------------------------------------------------------
    /* Parameters for Problem*/
//----------------------------------------------------------------------
    /* Parameters for Problem*/
    public static final String MAX_TP = "max-test-point";
    public static final String MIN_TP = "min-test-point";
    public static final String NUM_TP = "number-of-test-points";
    public static final String MIN_ER = "zero-error";
    public float maxTP;
    public float minTP;
    public float minER;
    public int numTP;
    public float[] testPoints;
    // From Regression
    //public double currentValue;
    //public RegressionData input;
    //public double inputs[];
    //public double outputs[];

    
    public Object clone()
    {
        MyRegression myobj = (MyRegression) (super.clone());

        myobj.input = (RegressionData)(input.clone());
        return myobj;
     }
//----------------------------------------------------------------------
/** Setup parameters */
    public void setup(final EvolutionState state, final Parameter base)
    {
        // very important, remember this
        super.setup(state,base);

        //----------------------------------------------------------------------
        // Parameters for regression Problem
        maxTP = state.parameters.getFloat(base.push(MAX_TP),null,1);
        minTP = state.parameters.getFloat(base.push(MIN_TP),null,-1);
        minER = state.parameters.getFloat(base.push(MIN_ER),null,0.01F);
        numTP = state.parameters.getInt(base.push(NUM_TP),null,100);
        if (numTP<1) state.output.fatal("Test points size must be an integer greater than 0"); 

        // set up our input -- don't want to use the default base, it's unsafe
        input = (RegressionData) state.parameters.getInstanceForParameterEq(
            base.push(P_DATA), null, RegressionData.class);
        input.setup(state,base.push(P_DATA));

        /** Create test points */
        selectObjTP();
    }

//----------------------------------------------------------------------
// Create objective test points for evaluation 
    public void selectObjTP()
    {
        testPoints = new float[numTP];
        float inc = (maxTP - minTP) / ((float)(numTP-1));
        //System.out.print("Test points: ");
        for(int i=0; i<numTP; i++){
            testPoints[i] = minTP + ((float)i) * inc;
            //System.out.print(testPoints[i]+" ");
        }
        //System.out.print("\n");
    }
//----------------------------------------------------------------------
// preprocess and postprocess
    public void preprocessPopulation( final EvolutionState state, Population pop )
    {
        // Set fitness = 0
        for( int i = 0 ; i < pop.subpops.length ; i++ )
            for( int j = 0 ; j < pop.subpops[i].individuals.length ; j++ )
                ((SimpleFitness)(pop.subpops[i].individuals[j].fitness)).setFitness( state, 0, false );
    }

    public void postprocessPopulation( final EvolutionState state, Population pop )
    {
        // Evaluate solutions againt objective test points
        int ntp = testPoints.length;
        int nsol = pop.subpops[1].individuals.length;
        float[] objf = evalObjFitness(state, pop);
        
        // Output obj/subj fitness
        MyExpOutput myout = new MyExpOutput(state);
        myout.printFit(state, pop, objf);
        
        // Set evluated = true
        for( int i = 0 ; i < pop.subpops.length ; i++ ){
            for( int j = 0 ; j < pop.subpops[i].individuals.length ; j++ )
            {
                pop.subpops[i].individuals[j].evaluated = true;
            }    
        }
        // Check GP tree size for testing
/*        int GPTreeSize = 0;
        for( int j = 0 ; j < pop.subpops[1].individuals.length ; j++ )
        {
            GPTreeSize += pop.subpops[1].individuals[j].size();
        }
        System.out.println(state.generation+"  AveTreeSize: " + ((float)GPTreeSize/(float)pop.subpops[1].individuals.length));
*/
    }
//----------------------------------------------------------------------
/** Evaluation */
    /* Evaluate one individual against one testpoint */
    public float eval1on1(final EvolutionState state,
                        final GPIndividual ind,
                        final float indTP,
                        final int threadnum)
    {
        // The true value
        float fValue = func(indTP);
        // Estimated value
        currentValue = (double)indTP;
        ((GPIndividual)ind).trees[0].child.eval(state,threadnum,
                                     input,stack,((GPIndividual)ind),this);
        // Compute estimate error
        float result = Math.abs(fValue - (float)input.x);
        
        // Get rid of numbers that are too big/small
        final float PROBABLY_ZERO = 1.11E-15F;
        final float BIG_NUMBER = 1.0e15F;
        if (! (result < BIG_NUMBER ) )    result = BIG_NUMBER;
        else if (result < PROBABLY_ZERO)    result = 0.0F;
        
        // return ((result<=minER)?1:0);
        return (result);
    }

    /* Evaluates the individuals found in ind together. */
    public void evaluate(final EvolutionState state,
                         final Individual[] ind,  // the individuals to evaluate together
                         final boolean[] updateFitness,  // should this individuals' fitness be updated?
                         final boolean countVictoriesOnly, // can be neglected in cooperative coevolution
                         final int threadnum)
   {
        // Check number of number individuals: two
        if( ind.length != 2 || updateFitness.length != 2 )
            state.output.fatal( "The InternalSumProblem evaluates only two individuals at a time." );

        // Check genome type: boolean array and GPIndividual
        if( ! ( ind[0] instanceof FloatVectorIndividual ) )
            state.output.fatal( "The test-point individuals in MyParity problem should be BitVectorIndividuals." );
        if( ! ( ind[1] instanceof GPIndividual ) )
            state.output.fatal( "The solution individuals in MyParity problem should be GPIndividuals." );

        // Float individual for test points
        float temp = ((FloatVectorIndividual)ind[0]).genome[0];
        //System.out.print(temp+", ");

        // GP individual for solutions
        GPIndividual temp2 = (GPIndividual)ind[1];

        // Calculate interaction
        float interaction = -1*eval1on1(state,temp2,temp,threadnum);
    
        // Update fitness
        if( updateFitness[1] ){
            ((SimpleFitness)(ind[1].fitness)).setFitness( state, (float)(interaction), false );
        }
        //if( updateFitness[1] ){
        //    KozaFitness f = ((KozaFitness)ind[1].fitness);
        //    f.setStandardizedFitness(state, interaction);
        //    f.hits = interaction;
        //}
   }
//----------------------------------------------------------------------
// Supplemental methods
//----------------------------------------------------------------------
  // Evaluate objective fitness
  public float[] evalObjFitness( final EvolutionState state, 
                                 final Population pop )
  {
        // Evaluate solutions againt objective test points
        int ntp = testPoints.length;
        int nsol = pop.subpops[1].individuals.length;
        float[] objFit = new float[nsol];
        // For retrieving rules
        GPIndividual temp = new GPIndividual();

        // Output
        for (int j = 0; j<(nsol); j++)        // the first nsol-1 solutions
        {
            objFit[j] = 0;
            // Copy the solution
            temp = (GPIndividual)pop.subpops[1].individuals[j].clone();
            // Evaluate against all-test-points
            
            for (int i = 0; i < ntp; i++)
            {
               objFit[j] -= (float)eval1on1(state, temp, testPoints[i], 0);
            }
      
        }
        return (objFit);
  }
}
